package cn.dengta.context.rpc;

import java.util.concurrent.Executor;
import java.util.function.Function;
import java.util.function.Supplier;

import cn.dengta.common.util.SpringUtil;
import com.alibaba.nacos.api.config.listener.Listener;
import lombok.Getter;
import lombok.Setter;
import me.codeplayer.util.Encrypter;
import me.codeplayer.util.StringUtil;
import org.springframework.beans.factory.InitializingBean;

@Getter
@Setter
public class CachedConfig<T> {

	final String key;
	final Supplier<T> loader;

	transient T value;
	transient String md5;

	final Function<? super T, String> serializer;
	final Function<String, T> deserializer;

	final Listener listener = new Listener() {
		@Override
		public Executor getExecutor() {
			return SpringUtil.getTaskExecutor();
		}

		@Override
		public void receiveConfigInfo(String configInfo) {
			CachedConfigManager.LOGGER.info("接收到新的 Nacos 配置：{} => {}", key, configInfo);
			md5 = Encrypter.md5(StringUtil.toString(configInfo));
			value = deserialize(configInfo);
		}
	};

	CachedConfig(String key, Supplier<T> loader,
	             Function<? super T, String> serializer, Function<String, T> deserializer) {
		this.key = key;
		this.loader = loader;
		this.serializer = serializer;
		this.deserializer = deserializer;

		CachedConfigManager.tryInitConfig(this);
	}

	void init(CachedConfigManager manager) {
		final String dataId = manager.dataId(key);
		String text = manager.initAndWatch(dataId, 3000L, listener);
		CachedConfigManager.LOGGER.info("初始化加载 Nacos 配置：{} => {}", dataId, text);
		final boolean absent = text == null;
		if (absent) {
			this.value = loader.get();
			text = StringUtil.toString(serializer.apply(this.value));
		} else {
			this.value = deserialize(text);
		}
		this.md5 = Encrypter.md5(text);
		if (absent) {
			manager.publishConfig(dataId, text, this.md5);
		}
	}

	public T get() {
		if (this.md5 == null) { // 尚未初始化
			if (!CachedConfigManager.tryInitConfig(this)) {
				CachedConfigManager.LOGGER.warn("配置尚未完成初始化：" + key);
				return loader.get();
			}
		}
		return value;
	}

	public void set(T value) {
		final String text = StringUtil.toString(serializer.apply(value));
		final CachedConfigManager manager = CachedConfigManager.instance;
		if (manager.publishConfig(manager.dataId(key), text, this.md5)) {
			this.md5 = Encrypter.md5(text);
		}
		this.value = value;
	}

	public T deserialize(String json) {
		final T obj = deserializer.apply(json);
		if (obj instanceof InitializingBean) {
			try {
				((InitializingBean) obj).afterPropertiesSet();
			} catch (Exception e) {
				throw new IllegalArgumentException(e);
			}
		}
		return obj;
	}

}
